using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using FytSoa.ApiService.AppAdmin.Dto;
using FytSoa.Application.Filters;
using FytSoa.Application.Sys;
using FytSoa.Domain.Core.Result;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json;

namespace FytSoa.ApiService.Filters
{
    /// <summary>
    /// 方法过滤器
    /// </summary>
    public class AopActionFilter:IAsyncActionFilter
    {
        private readonly ISysLogService _logService;
        public AopActionFilter (ISysLogService logService) {
            _logService = logService;
        }
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            if (!context.ModelState.IsValid)
            {
                context.Result = new JsonResult(JResult<string>.Error("参数不能为空~"));
                return;
            }
            // 判断是否写日志
            if (!ShouldSaveAudit(context))
            {
                await next();
                return;
            }
            //接口Type
            var type = (context.ActionDescriptor as ControllerActionDescriptor)?.ControllerTypeInfo.AsType ();
            //方法信息
            var method = (context.ActionDescriptor as ControllerActionDescriptor)?.MethodInfo;
            //方法参数
            var arguments = context.ActionArguments;
            //开始计时
            var stopwatch = Stopwatch.StartNew ();
            var module = type != null ? type.FullName : "";
            //构建实体
            var logInfo = new SysLogDto() {
                LogType = module != null && module.Contains ("Sys.LoginController") ? 1 : 2,
                Module = type != null ? type.FullName : "",
                Method = context.HttpContext.Request.Method,
                OperateUser = context.HttpContext.User.Claims.FirstOrDefault (o => o.Type == nameof (OperatorUser.FullName))?.Value,
                Parameters = JsonConvert.SerializeObject (arguments),
                IP = context.HttpContext.Connection.RemoteIpAddress?.ToString (),
                Address = context.HttpContext.Request.Path + context.HttpContext.Request.QueryString,
                Browser = context.HttpContext.Request.Headers["User-Agent"].ToString (),
            };
            //Logger.Info(JsonConvert.SerializeObject(logInfo));
            ActionExecutedContext result = null;
            try {
                result = await next ();
                if (result.Exception != null && !result.ExceptionHandled) {
                    logInfo.Message = result.Exception.Message;
                }
            } catch (Exception ex) {
                logInfo.LogType = 3;
                logInfo.Message = ex.Message;
                throw new Exception("审计日志采集出现异常："+ex.Message);
            } finally {
                stopwatch.Stop ();
                logInfo.ExecutionDuration = Convert.ToInt32 (stopwatch.Elapsed.TotalMilliseconds);
                if (result != null)
                {
                    logInfo.ReturnValue = result.Result switch
                    {
                        ObjectResult objectResult => JsonConvert.SerializeObject(objectResult.Value),
                        JsonResult jsonResult => JsonConvert.SerializeObject(jsonResult.Value),
                        ContentResult contentResult => contentResult.Content,
                        _ => logInfo.ReturnValue
                    };
                    logInfo.ReturnValue = logInfo.ReturnValue?.Replace ("\\", "");
                }
                //保存日志信息
                await _logService.AddAsync (logInfo);
            }
        }
        
        /// <summary>
        /// 是否需要记录审计
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private bool ShouldSaveAudit(ActionExecutingContext context)
        {
            if (context.ActionDescriptor is not ControllerActionDescriptor descriptor)
                return false;
            var methodInfo = descriptor?.MethodInfo;

            if (methodInfo == null || !methodInfo.IsPublic)
            {
                return false;
            }
            
            if (methodInfo.GetCustomAttribute<AuditedAttribute>() != null)
            {
                return true;
            }

            if (methodInfo.GetCustomAttribute<DisableAuditingAttribute>() != null)
            {
                return false;
            }

            var classType = methodInfo.DeclaringType;
            if (classType == null) return false;
            if (classType.GetTypeInfo().GetCustomAttribute<AuditedAttribute>() != null)
            {
                return true;
            }
            
            return classType.GetTypeInfo().GetCustomAttribute<DisableAuditingAttribute>() == null;
        }
    }
}